Skip to content

feat(hosting-cli): add reflex cloud gcp deploy for Cloud Run#6450

Merged
masenf merged 7 commits into
mainfrom
cloudrun-deploy-cli
May 12, 2026
Merged

feat(hosting-cli): add reflex cloud gcp deploy for Cloud Run#6450
masenf merged 7 commits into
mainfrom
cloudrun-deploy-cli

Conversation

@Kastier1
Copy link
Copy Markdown
Contributor

@Kastier1 Kastier1 commented May 4, 2026

ENG-9469

Summary

  • New reflex cloud gcp deploy command that fetches a Dockerfile + bash deploy script from flexgen (GET /api/v1/cli/gcp-cloud-run-manifest), writes the Dockerfile into the user's source directory, prints the script, and runs it via bash after a single y/n confirmation.
  • Pre-flights bash/gcloud/docker on PATH and an active gcloud account, with actionable error messages. Surfaces a clear "Enterprise tier required" message on a 403 from flexgen.
  • Deploy parameters (--gcp-project required; --region, --service-name, --ar-repo, --version) are passed to the script via GCP_PROJECT, GCP_REGION, SERVICE_NAME, AR_REPO, VERSION environment variables. --dry-run prints the manifest without writing or executing; --overwrite-dockerfile skips the existing-Dockerfile prompt.

Auth model

  • Reflex hosting auth uses the existing X-API-Token flow (hosting.get_authenticated_client).
  • GCP credentials never leave the user's machine — the CLI relies on the user's existing gcloud login (detected via gcloud auth list --filter=status:ACTIVE).

Test plan

  • uv run pytest tests/units/reflex_cli — 152 tests pass (12 new in tests/units/reflex_cli/v2/test_gcp.py).
  • uv run ruff check, uv run ruff format, uv run pyright clean on changed files.
  • uv run pre-commit run --files <changed> passes (ruff-format, ruff-check, codespell, update-pyi-files, pyright).
  • End-to-end run against a real flexgen + GCP project (requires Enterprise-tier account).

🤖 Generated with Claude Code

Fetches a Dockerfile and bash deploy script from flexgen
(`GET /api/v1/cli/gcp-cloud-run-manifest`), writes the Dockerfile into the
user's source directory, prints the script, and runs it via bash after the
user confirms. Pre-flights `bash`/`gcloud`/`docker` on PATH and an active
gcloud account, and surfaces a clear message on 403 (Enterprise tier
required). Deploy parameters (project, region, service name, AR repo,
version) are passed via env vars to the script.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@Kastier1 Kastier1 requested a review from a team as a code owner May 4, 2026 20:21
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 4, 2026

Merging this PR will not alter performance

✅ 24 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing cloudrun-deploy-cli (74b114d) with main (bc434f7)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 4, 2026

Greptile Summary

This PR adds a reflex cloud deploy --gcp command that fetches a Dockerfile + bash deploy script from the Reflex API, embeds the Dockerfile in a generated cloudbuild.yaml tempfile, rewrites the script's gcloud builds submit invocation to use --config=, and runs the script with a restricted environment allowlist instead of forwarding the full parent-process environment.

  • gcp.py: New module containing the Click command, pre-flight checks (bash/gcloud/docker/active GCP account), manifest fetch, cloudbuild.yaml generation via a single-quoted heredoc with $$-escaped Dockerfile content, gcloud builds submit rewrite logic, environment allowlisting, and tempfile lifecycle management.
  • deployments.py: Registers the new command under hosting_cli as deploy; no pre-existing deploy command was present so no name conflict.
  • Docs + sidebar: Adds a GCP Cloud Run guide and wires it into the hosting sidebar.

Confidence Score: 5/5

Safe to merge; the previously flagged environment-passthrough security issue is addressed via an explicit allowlist, and the new deploy flow is well-tested with 12 targeted unit tests.

The core deploy logic is sound: pre-flight checks, manifest fetching, Dockerfile-in-heredoc embedding, dollar-sign escaping for Cloud Build substitutions, and tempfile cleanup are all implemented correctly. The only non-trivial concerns are a dead DOCKERFILE_NAME constant and the implicit coupling between the client-side rewrite and the IMAGE variable name in the server script, neither of which represents a current defect on the changed path.

packages/reflex-hosting-cli/src/reflex_cli/v2/gcp.py — specifically the _rewrite_builds_submit function's implicit dependency on the server script's IMAGE variable name.

Important Files Changed

Filename Overview
packages/reflex-hosting-cli/src/reflex_cli/v2/gcp.py New GCP Cloud Run deploy module; well-structured with proper env allowlisting, tempfile cleanup, and error handling. DOCKERFILE_NAME constant is defined but unused. The _rewrite_builds_submit function tightly couples the hardcoded IMAGE variable name to the server-side script format without validation.
packages/reflex-hosting-cli/src/reflex_cli/v2/deployments.py Registers the new GCP deploy command under hosting_cli as 'deploy'; no pre-existing deploy command was present so no name conflict.
tests/units/reflex_cli/v2/test_gcp.py 12 new tests covering happy path, aborts, dry-run, env restriction, missing prerequisites, 403 handling, and script rewrite logic; good coverage of the environment allowlist behavior.
docs/hosting/deploy-to-gcp.md New documentation page; clearly explains the security model, Cloud Build heredoc mechanism, non-interactive CI usage, and troubleshooting steps.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as reflex cloud deploy --gcp
    participant Preflight as Pre-flight Checks
    participant Reflex as Reflex API
    participant Bash as bash -s (restricted env)
    participant GCP as Google Cloud

    User->>CLI: reflex cloud deploy --gcp --gcp-project X
    CLI->>Preflight: check bash / gcloud / docker on PATH
    CLI->>Preflight: "gcloud auth list --filter=status:ACTIVE"
    Preflight-->>CLI: OK / Exit(1)
    CLI->>Reflex: GET /api/v1/cli/gcp-cloud-run-manifest (X-API-TOKEN)
    Reflex-->>CLI: "{ dockerfile, deploy_command }"
    Note over CLI: _build_cloudbuild_yaml(dockerfile) embeds Dockerfile via heredoc
    Note over CLI: _rewrite_builds_submit(script) replaces gcloud builds submit
    CLI->>User: print rewritten script + env vars
    alt interactive mode
        User->>CLI: y / n
    end
    CLI->>CLI: write cloudbuild.yaml to tempfile
    CLI->>Bash: bash -s (allowlisted env only)
    Bash->>GCP: gcloud services enable ...
    Bash->>GCP: gcloud artifacts repositories create ...
    Bash->>GCP: "gcloud builds submit --config=cloudbuild.yaml"
    GCP->>GCP: Cloud Build writes Dockerfile, builds and pushes image
    Bash->>GCP: gcloud run deploy ...
    GCP-->>User: Service URL
    CLI->>CLI: delete tempfile (always)
Loading

Reviews (3): Last reviewed commit: "update docs" | Re-trigger Greptile

Comment thread packages/reflex-hosting-cli/src/reflex_cli/v2/gcp.py Outdated
Comment thread packages/reflex-hosting-cli/src/reflex_cli/v2/gcp.py Outdated
Comment thread packages/reflex-hosting-cli/src/reflex_cli/v2/gcp.py
Kastier1 and others added 2 commits May 7, 2026 10:30
…act constants

Address PR feedback:

- Restrict the deploy script's environment to an allowlist of host vars
  (PATH, HOME, gcloud/docker config, proxy/TLS) plus the explicit deploy
  overrides. Prevents a tampered or compromised flexgen manifest from
  exfiltrating unrelated host secrets like AWS_*/GITHUB_TOKEN.
- Add --interactive/--no-interactive (default true) so the command works
  in CI. In non-interactive mode the run prompt is skipped, and an
  existing Dockerfile errors out unless --overwrite-dockerfile is set.
- Extract env-var keys and manifest field names into module-level
  constants per project convention.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…p`, add docs

Flatten the `gcp` group into a single `deploy` command with a `--gcp`
target flag so the surface can grow to other targets without nesting.
`--gcp-project` becomes optional at the Click level and is validated
in-function so the missing-target error fires first.

Add a hosting doc (with Enterprise-only callout) covering prerequisites,
options, what gets created in the GCP project, the env-allowlist
security model, CI usage, and troubleshooting. Wire it into the
sidebar's Self Hosting section and add `Gcp` -> `GCP` to the sidebar
acronym map.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Kastier1 Kastier1 requested a review from Alek99 as a code owner May 8, 2026 01:01
@Kastier1
Copy link
Copy Markdown
Contributor Author

Kastier1 commented May 8, 2026

@greptileai

@Kastier1 Kastier1 force-pushed the cloudrun-deploy-cli branch from b3cb739 to 16a39ee Compare May 8, 2026 18:02
Kastier1 and others added 3 commits May 11, 2026 13:41
…p deploy

The Dockerfile no longer touches disk anywhere near the user's source —
it's embedded (base64) as an inline `docker build` step inside a Cloud
Build config written to a tempfile, and the flexgen script's
`gcloud builds submit --tag X .` invocation is rewritten in-memory to
`--config="${REFLEX_CLOUDBUILD_YAML}" --substitutions=_IMAGE="${IMAGE}"`.

The script runs with cwd = the user's source dir, so the user's tree is
the Cloud Build upload context. No temp dir of symlinks, no source-tree
mutation. The cloudbuild.yaml tempfile is removed after the deploy.

If `gcloud builds submit` can't be located in the manifest's script
(format drift on flexgen's side), the rewrite errors out clearly so the
breakage surfaces immediately rather than half-running.

Verified end-to-end against a real GCP project: 1m53s Cloud Build, new
Cloud Run revision deployed and serving traffic, source Dockerfile
timestamp unchanged, tempfile cleaned up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Kastier1
Copy link
Copy Markdown
Contributor Author

@greptileai

Copy link
Copy Markdown
Collaborator

@masenf masenf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can bring this in, but i think it would be better to have the flexgen service return the cloudbuild.yaml and bake the gcloud scripting pieces into the cli itself, rather than a bash script.

this still gives us control on the server side to run commands in the builder environment, but notably the user is NOT running some shell script from the web. it would also help with cross platformness, as we probably do want to continue supporting windows (and bash/docker is not always available there)

Comment on lines +205 to +210
bash_path = shutil.which("bash")
if not bash_path:
console.error(
"`bash` was not found on PATH; required to run the deploy script."
)
raise click.exceptions.Exit(1)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we actually need bash? this makes it kind of un-fun to run on windows

Comment on lines +221 to +225
if not shutil.which("docker"):
console.error(
"The `docker` CLI was not found on PATH; required to build the image."
)
raise click.exceptions.Exit(1)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we actually use docker? i thought the builds were coordinated by gcloud builders

@masenf masenf merged commit 2c9f76c into main May 12, 2026
69 checks passed
@masenf masenf deleted the cloudrun-deploy-cli branch May 12, 2026 00:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants